
/***************************************************************************
 *   jell - library                                                        *
 *                                                                         *
 *   Copyright (C) 2008 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/

#ifndef JEFI_JELL_EXCEPTION_HPP
#define JEFI_JELL_EXCEPTION_HPP

#include <stdexcept>
#include <ostream>
#include <string>

#include "jell/string.hpp"

namespace   jell
{

    class   JellException : public std::runtime_error
    {
    public  :
        typedef std::runtime_error  Parent;

    public  :
        JellException(const std::string& msg,
                    int error=0, int line=0, const char* file=NULL, const char* func=NULL)
            :   Parent(msg),
                m_error(error), m_line(line), m_file(file), m_func(func),
                m_prev(NULL)
        {
        }

        JellException(const std::string& msg, const JellException& ex,
                    int error=0, int line=0, const char* file=NULL, const char* func=NULL)
            :   Parent(msg),
                m_error(error), m_line(line), m_file(file), m_func(func),
                m_prev(new JellException(ex))
        {
        }

        JellException(const JellException& ex)
            :   Parent(ex),
                m_error(ex.m_error), m_line(ex.m_line), m_file(ex.m_file), m_func(ex.m_func)
        {
            m_prev = ex.m_prev ? new JellException(*ex.m_prev) : NULL;
        }

        ~JellException() throw ()
        {
            if (NULL != m_prev) {
                delete m_prev;
            }
        }

    public  :
        int code() const { return m_error; }
        int line() const { return m_line; }
        const char* file() const { return m_file; }
        const char* func() const { return m_func; }

        JellException*  prev() const { return m_prev; }

    private :
        const JellException&    operator=(const JellException&);

    private :
        int         m_error;
        int         m_line;
        const char* m_file;
        const char* m_func;

        JellException*  m_prev;
    };  // class JellException


    #ifndef __FUNC__
    #ifdef  __GNUC__
    #define __FUNC__    __PRETTY_FUNCTION__
    #else
    #define __FUNC__    __func__
    #endif
    #endif  // __FUNC__

    #define THROW(_code, _msg) \
        throw JellException(_msg, _code, __LINE__, __FILE__, __FUNC__)

    #define THROW_FMT(_code, _fmt, _args...) \
        throw JellException(String::format(_fmt, ##_args), _code, __LINE__, __FILE__, __FUNC__)

    #define THROW_EX(_code, _ex, _msg) \
        throw JellException(_msg, _ex, _code, __LINE__, __FILE__, __FUNC__)

    #define THROW_EX_FMT(_code, _ex, _fmt, _args...) \
        throw JellException(String::format(_fmt, ##_args), _ex, _code, __LINE__, __FILE__, __FUNC__)

    #define EXTERNAL    0xFFFF

    #define TRY(_s_, _code_) do { \
        try { _code_; } \
        catch (JellException &e) { \
            THROW_EX(e.code(), e, _s_); \
        } \
        catch (std::bad_alloc &e) { \
            THROW(EXTERNAL, "bad alloc " _s_); \
        } \
        catch (std::exception &e) { \
            THROW_FMT(EXTERNAL, "JellExternal exception " _s_ ": %s",  e.what()); \
        } \
    } while (0)


    std::ostream&   operator<<(std::ostream& out, const JellException& ex);


}   // namespace jell


#endif  // JEFI_JELL_EXCEPTION_HPP


